package folder.main.exp0.controllers.heuristic;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;

import folder.main.exp0.controllers.ControllerFinding;
import folder.model.network.Network;
import folder.model.node.Node;

public class Heuristic extends ControllerFinding
{
	protected Network network;
	protected double delays[][];
	protected int T;
	
	public Heuristic( Network network, double delays[][], int T )
	{
		this.network = network;
		this.delays = delays;
		this.T = T;
	}
	
	protected List< Node > arranging(){ return null; }
	
	protected LinkedHashMap< Node, Node > matching( List< Node > arranging )
	{
		LinkedHashMap< Node, Node > matching = new LinkedHashMap< Node, Node >();
		List< Node > controllers = new ArrayList< Node >();
		List< Node > uncontrolleds = new ArrayList< Node >();
		
		for( Node node: network.getNodes() )
			uncontrolleds.add( node );
		
		while( !uncontrolleds.isEmpty() && !arranging.isEmpty() )
		{
			Node candidate = arranging.get( 0 );
			
			if( arranging.contains( candidate ) )
				arranging.remove( candidate );
			
			List< Node > controllersToRemove = new ArrayList< Node >();
			
			for( Node uncontrolled: uncontrolleds )
				if( !uncontrolled.equals( candidate ) && delays[ candidate.getIdentifier() ][ uncontrolled.getIdentifier() ] <= T )
				{
					Node controllerToRemove = null;
					
					if( matching.containsKey( candidate ) )
					{
						controllerToRemove = matching.get( candidate );
						matching.remove( candidate );
					}
					
					if( controllerToRemove != null && !matching.containsValue( controllerToRemove ) )
					{
						if( controllers.contains( controllerToRemove ) )
							controllers.remove( controllerToRemove );
						
						double minDistance = Integer.MAX_VALUE;
						Node minController = null;
						
						for( Node controller: controllers )
						{
							double distance = delays[ controller.getIdentifier() ][ controllerToRemove.getIdentifier() ];
							
							if( distance < minDistance && distance <= T )
							{
								minDistance = distance;
								minController = controller;
							}
						}
						
						if( minController != null )
							matching.put( controllerToRemove, minController );
						else
							controllersToRemove.add( controllerToRemove );
					}
					
					matching.put( uncontrolled, candidate );
					
					if( !controllers.contains( candidate ) )
						controllers.add( candidate );
				}
			
			if( !controllersToRemove.isEmpty() )
				for( Node controllerToReturn: controllersToRemove )
					uncontrolleds.add( controllerToReturn );
			
			Iterator< Entry< Node, Node > > iterator2 = matching.entrySet().iterator();
			
			while( iterator2.hasNext() )
			{
				Entry< Node, Node > entry = iterator2.next();
				Node controlled = entry.getKey();
				Node controller = entry.getValue();
				
				if( uncontrolleds.contains( controlled ) )
					uncontrolleds.remove( controlled );
				
				if( uncontrolleds.contains( controller ) )
					uncontrolleds.remove( controller );
			}
		}
		
		if( !uncontrolleds.isEmpty() )
			matching = null;
		
		return matching;
	}
	
	protected LinkedHashMap< Node, Node > minimizing( LinkedHashMap< Node, Node > matching )
	{
		LinkedHashMap< Node, Node > minMatching = new LinkedHashMap< Node, Node >();
		minMatching.putAll( matching );
		
		Iterator< Entry< Node, Node > > iterator = matching.entrySet().iterator();
		
		List< Node > controllers = this.computeControllers( matching );
		
		iterator = matching.entrySet().iterator();
		
		while( iterator.hasNext() )
		{	
			Entry< Node, Node > entry = iterator.next();
			Node controlled = entry.getKey();
			Node controller = entry.getValue();
						
			double minDelay = delays[ controlled.getIdentifier() ][ controller.getIdentifier() ];
			Node newController = null;
			
			for( Node candidate: controllers )
				if( delays[ controlled.getIdentifier() ][ candidate.getIdentifier() ] <= minDelay )
				{
					minDelay = delays[ controlled.getIdentifier() ][ candidate.getIdentifier() ];
					newController = candidate;
				}
			
			if( newController != null )
				minMatching.put( controlled, newController );
		}
		
		List< Node > minControllers = this.computeControllers( minMatching );
		
		if( minControllers.size() < controllers.size() )
		{
			for( Node controller: controllers )				
				if( !minControllers.contains( controller ) )
				{
					double minDelay = T;
					Node minDelayController = null;
					
					for( Node minController: minControllers )
					{
						double delay = delays[ controller.getIdentifier() ][ minController.getIdentifier() ];
						
						if( delay <= minDelay )
						{
							minDelay = delay;
							minDelayController = minController;
						}
					}
					
					if( minDelayController != null )
						minMatching.put( controller, minDelayController );
					else
					{
						minMatching = matching;
						break;
					}
				}
		}
		
		return minMatching;
	}
	
}
